#include "msgq/msgq_adapter.h"
#include <algorithm>
#include <cstring>
#include "log/logger.h"

MsgQ::MsgQ(const std::string &path, size_t size)
    : queue_(nullptr, msgq_close_queue), endpoint_(path), is_publisher_(false),
      is_initialized_(false) {

  msgq_queue_t *raw_queue = new msgq_queue_t();

  if (msgq_new_queue(raw_queue, path.c_str(), size) != 0) {
    delete raw_queue;
    JC_ERROR("Failed to create message queue for path: {}", path);
  }

  queue_.reset(raw_queue);
}

MsgQ::~MsgQ() {}

MsgQ::MsgQ(MsgQ &&other) noexcept
    : queue_(std::move(other.queue_)), endpoint_(std::move(other.endpoint_)),
      is_publisher_(other.is_publisher_),
      is_initialized_(other.is_initialized_) {
  other.is_initialized_ = false;
}

MsgQ &MsgQ::operator=(MsgQ &&other) noexcept {
  if (this != &other) {
    queue_ = std::move(other.queue_);
    endpoint_ = std::move(other.endpoint_);
    is_publisher_ = other.is_publisher_;
    is_initialized_ = other.is_initialized_;
    other.is_initialized_ = false;
  }
  return *this;
}

void MsgQ::init_publisher() {
  if (!queue_) {
    throw std::runtime_error("Queue not initialized");
  }
  msgq_init_publisher(queue_.get());
  is_publisher_ = true;
  is_initialized_ = true;
}

void MsgQ::init_subscriber() {
  if (!queue_) {
    throw std::runtime_error("Queue not initialized");
  }
  msgq_init_subscriber(queue_.get());
  is_publisher_ = false;
  is_initialized_ = true;
}

bool MsgQ::send(const void *data, size_t size) {
  if (!is_publisher_ || !is_initialized_ || !queue_) {
    return false;
  }

  MsgData msg_data(data, size);
  return msgq_msg_send(msg_data.get(), queue_.get()) > 0;
}

bool MsgQ::send(const std::string &message) {
  return send(message.c_str(), message.size()) > 0;
}

bool MsgQ::all_readers_updated() const {
  return queue_ ? msgq_all_readers_updated(queue_.get()) : false;
}

bool MsgQ::receive(std::vector<uint8_t> &data) {
  if (is_publisher_ || !is_initialized_ || !queue_) {
    return false;
  }

  MsgData msg_data;
  int result = msgq_msg_recv(msg_data.get(), queue_.get());
  if (result > 0 && !msg_data.empty()) {
    data.resize(msg_data.size());
    std::memcpy(data.data(), msg_data.data(), msg_data.size());
    return true;
  }
  return false;
}

bool MsgQ::receive(std::string &message) {
  std::vector<uint8_t> data;
  if (receive(data)) {
    message.assign(reinterpret_cast<const char *>(data.data()), data.size());
    return true;
  }
  return false;
}

bool MsgQ::is_message_ready() const {
  return queue_ ? (msgq_msg_ready(queue_.get()) > 0) : false;
}

void MsgQ::reset_reader() {
  if (!is_publisher_ && queue_) {
    msgq_reset_reader(queue_.get());
  }
}

MsgData::MsgData(size_t size) : msg_{0, nullptr} {
  if (size > 0) {
    if (msgq_msg_init_size(&msg_, size) != 0) {
      throw std::runtime_error("Failed to initialize message data");
    }
  }
}

MsgData::MsgData(const void *data, size_t size) : MsgData(size) {
  if (data && size > 0 && msg_.data) {
    std::memcpy(msg_.data, data, size);
  }
}

MsgData::MsgData(const std::string &str) : MsgData(str.c_str(), str.size()) {}

MsgData::~MsgData() { msgq_msg_close(&msg_); }

MsgData::MsgData(MsgData &&other) noexcept : msg_(other.msg_) {
  other.msg_ = {0, nullptr};
}

MsgData &MsgData::operator=(MsgData &&other) noexcept {
  if (this != &other) {
    msgq_msg_close(&msg_);
    msg_ = other.msg_;
    other.msg_ = {0, nullptr};
  }
  return *this;
}

std::string MsgData::to_string() const {
  if (msg_.data && msg_.size > 0) {
    return std::string(static_cast<const char *>(msg_.data), msg_.size);
  }
  return "";
}
